package org.jflame.captcha.creator;

import org.jflame.captcha.Captcha;
import org.jflame.captcha.validator.SingleVerifiable;

import java.util.function.Function;

import org.apache.commons.lang3.RandomUtils;

/**
 * 正数数学公式验证码生成器
 * 
 * @author charles.zhang
 */
public class MathCaptchaCreator extends AbstractTextCaptchaCreator<Integer> {

    /**
     * 生成的随机数的最大数值
     */
    private final int max;

    /**
     * 构造函数,默认最大数值是10,即生成10以内运算
     */
    public MathCaptchaCreator() {
        max = 10;
    }

    public MathCaptchaCreator(int max) {
        this.max = max;
    }

    @Override
    public Captcha<SingleVerifiable<Integer>,String> create() {
        int left;
        int right;
        MathOperation oper = MathOperation.random();
        if (oper == MathOperation.DIVIDE) {
            // 除法运算,随机生成被除数和商,再相乘得除数,避免出现相除有小数情况
            right = RandomUtils.nextInt(1, max);
            left = right * RandomUtils.nextInt(1, max / 3);
        } else if (oper == MathOperation.SUBTRACT) {
            left = RandomUtils.nextInt(1, max);
            right = RandomUtils.nextInt(0, left);// 避免出现负数
        } else {
            left = RandomUtils.nextInt(0, max);
            right = RandomUtils.nextInt(0, max);
        }
        String randomStr = left + oper.getSymbol() + right + "=";
        int r = oper.calc(left, right);

        return new Captcha<>(new SingleVerifiable<Integer>(r), randomStr);
    }

    @Override
    public SingleVerifiable<Integer> parse(String verifiableString) {
        return new SingleVerifiable<Integer>(Integer.valueOf(verifiableString));
    }

    enum MathOperation {

        ADD("+", (arr) -> arr[0] + arr[1]),
        SUBTRACT("-", (arr) -> arr[0] - arr[1]),
        DIVIDE("÷", (arr) -> arr[0] / arr[1]),
        MULTIPLY("x", (arr) -> arr[0] * arr[1]);

        private String symbol;
        private Function<int[],Integer> operation;

        private MathOperation(String symbol, Function<int[],Integer> operation) {
            this.symbol = symbol;
            this.operation = operation;
        }

        public String getSymbol() {
            return symbol;
        }

        public int calc(int leftOperand, int rightOperand) {
            return operation.apply(new int[] { leftOperand,rightOperand });
        }

        static MathOperation random() {
            MathOperation[] operations = MathOperation.values();
            int index = RandomUtils.nextInt(0, operations.length);
            return operations[index];
        }
    }

}
